#include "CoreArray.h"

int* CoreArray::getStart()
{
	return start;
}

int CoreArray::getWidth()
{
	return width;
}

int CoreArray::getHeight()
{
	return height;
}

int CoreArray::getLength()
{
	return length;
}

int CoreArray::getX(int* p)
{
	return (p - start) % width;
}

int CoreArray::getY(int* p)
{
	return (p - start) / width;
}

int* CoreArray::getPByDirection(int* p, Direction d)
{
	if (d == Direction::TOP) {
		return getTop(p);
	}
	else if (d == Direction::RIGHT) {
		return getRight(p);
	}
	else if (d == Direction::DOWN) {
		return getDown(p);
	}
	else if (d == Direction::LEFT) {
		return getLeft(p);
	}
	else {
		return nullptr;
	}
}

Direction CoreArray::reverse(Direction d) {
	if (d == Direction::TOP) {
		return Direction::DOWN;
	}
	else if (d == Direction::RIGHT) {
		return Direction::LEFT;
	}
	else if (d == Direction::DOWN) {
		return Direction::TOP;
	}
	else if (d == Direction::LEFT) {
		return Direction::RIGHT;
	}
	else {
		return Direction();
	}
}

int* CoreArray::getTop(int* p)
{
	if (getY(p) == 0)return nullptr;
	return p - width;
}

int* CoreArray::getRight(int* p)
{
	if (getX(p) == width - 1)return nullptr;
	return (p + 1);
}

int* CoreArray::getDown(int* p)
{
	if (getY(p) == height - 1)return nullptr;
	return p + width;
}

int* CoreArray::getLeft(int* p)
{
	if (getX(p) == 0)return nullptr;
	return (p - 1);
}

int CoreArray::getCarryNumByDirection(int* p, Direction d)
{
	int carryNum = 0;
	if (d == Direction::TOP) {
		carryNum = getHowMuchOnTop(p);
	}
	else if (d == Direction::RIGHT) {
		carryNum = getHowMuchOnRight(p);
	}
	else if (d == Direction::DOWN) {
		carryNum = getHowMuchOnDown(p);
	}
	else if (d == Direction::LEFT) {
		carryNum = getHowMuchOnLeft(p);
	}
	return carryNum;
}

bool CoreArray::isInRange(int* p)
{
	if (p >= start && p < end)return true;
	return false;
}

Direction CoreArray::isMoveTo(int* p, int* target)
{
	int n = p - target;
	if (n > 0 && n < width) {
		return Direction::LEFT;
	}
	else if (n > width) {
		return Direction::TOP;
	}
	else if (n<0 && n>-width) {
		return Direction::RIGHT;
	}
	else if (n < -width) {
		return Direction::DOWN;
	}
}

bool CoreArray::changeNum(int* p1, int i)
{
	if (!isInRange(p1))return false;
	*p1 = i;
	return true;
}

bool CoreArray::change0To2Or4(int* p)
{
	if (isInRange(p) && *p == 0) {
		*p = rand2Or4ByTime();
		return true;
	}
	return false;
}

ZeroArray CoreArray::refreshZeroArray()
{
	if (zeroArray.arr != nullptr)delete[] zeroArray.arr;
	zeroArray.length = 0;
	int** arr = new int* [length];
	for (int i = 0; i < length; i++) {
		int* current = start + i;
		if (*current == 0) {
			*(arr + zeroArray.length) = current;
			zeroArray.length++;
		}
	}
	zeroArray.arr = arr;
	return zeroArray;
}

ZeroArray CoreArray::getZeroArray()
{
	if (zeroArray.arr == nullptr) refreshZeroArray();
	return zeroArray;
}

int* CoreArray::findUpNonzero(int* p)
{
	int y = getY(p);
	int* current = p;
	for (int i = 0; i < y; i++) {
		current = getTop(current);
		if (*current != 0) return current;
	}
	return nullptr;
}

int* CoreArray::findRightNonzero(int* p)
{
	int x = getX(p);
	int* current = p;
	for (int i = 0; i < width - x - 1; i++) {
		current = getRight(current);
		if (*current != 0)return current;
	}
	return nullptr;
}

int* CoreArray::findDownNonzero(int* p)
{
	int y = getY(p);
	int* current = p;
	for (int i = 0; i < height - y - 1; i++) {
		current = getDown(current);
		if (*current != 0)return current;
	}
	return nullptr;
}

int* CoreArray::findLeftNonzero(int* p)
{
	int x = getX(p);
	int* current = p;
	for (int i = 0; i < x; i++) {
		current = getLeft(current);
		if (*current != 0)return current;
	}
	return nullptr;
}

int* CoreArray::findNonzeroByDirection(int* p, Direction d)
{
	int* pNonzero = nullptr;
	if (d == Direction::TOP) {
		pNonzero = findUpNonzero(p);
	}
	else if (d == Direction::RIGHT) {
		pNonzero = findRightNonzero(p);
	}
	else if (d == Direction::DOWN) {
		pNonzero = findDownNonzero(p);
	}
	else if (d == Direction::LEFT) {
		pNonzero = findLeftNonzero(p);
	}
	return pNonzero;
}

int* CoreArray::findTopMost(int* p)
{
	return getStart() + getX(p);
}

int* CoreArray::findRightMost(int* p)
{
	return getStart() + (getY(p) + 1) * width - 1;
}

int* CoreArray::findDownMost(int* p)
{
	return getStart() + (height - 1) * width + getX(p);
}

int* CoreArray::findLeftMost(int* p)
{
	return getStart() + getY(p) * width;
}

int* CoreArray::findDirectionMost(int* p, Direction d)
{
	int* pMost = nullptr;
	if (d == Direction::TOP) {
		pMost = findTopMost(p);
	}
	else if (d == Direction::RIGHT) {
		pMost = findRightMost(p);
	}
	else if (d == Direction::DOWN) {
		pMost = findDownMost(p);
	}
	else if (d == Direction::LEFT) {
		pMost = findLeftMost(p);
	}
	return pMost;
}

int* CoreArray::findTopSerialZeroLast(int* p)
{
	int* pTopMost = findTopMost(p);
	if (pTopMost == p)return nullptr;
	int* pCurrent = p;
	int* pZero = nullptr;
	while (pTopMost != pCurrent) {
		pCurrent = getTop(pCurrent);
		if (*pCurrent != 0)break;
		if (*pCurrent == 0) pZero = pCurrent;
	}
	return pZero;
}

int* CoreArray::findRightSerialZeroLast(int* p)
{
	int* pRightmost = findRightMost(p);
	if (pRightmost == p)return nullptr;
	int* pCurrent = p;
	int* pZero = nullptr;
	while (pRightmost != pCurrent) {
		pCurrent = getRight(pCurrent);
		if (*pCurrent != 0)break;
		if (*pCurrent == 0) pZero = pCurrent;
	}
	return pZero;
}

int* CoreArray::findDownSerialZeroLast(int* p)
{
	int* pLowest = findDownMost(p);
	if (pLowest == p)return nullptr;
	int* pCurrent = p;
	int* pZero = nullptr;
	while (pLowest != pCurrent) {
		pCurrent = getDown(pCurrent);
		if (*pCurrent != 0)break;
		if (*pCurrent == 0) pZero = pCurrent;
	}
	return pZero;
}

int* CoreArray::findLeftSerialZeroLast(int* p)
{
	int* pLeftmost = findLeftMost(p);
	if (pLeftmost == p)return nullptr;
	int* pCurrent = p;
	int* pZero = nullptr;
	while (pLeftmost != pCurrent) {
		pCurrent = getLeft(pCurrent);
		if (*pCurrent != 0)break;
		if (*pCurrent == 0) pZero = pCurrent;
	}
	return pZero;
}

int* CoreArray::findSerialZeroLastByDirection(int* p, Direction d)
{
	int* pMost = findDirectionMost(p, d);
	if (pMost == p)return nullptr;
	int* pCurrent = p;
	int* pZero = nullptr;
	while (pMost != pCurrent) {
		pCurrent = getPByDirection(pCurrent, d);
		if (*pCurrent != 0)break;
		else pZero = pCurrent;
	}
	return pZero;
}

int CoreArray::getHowMuchOnTop(int* p)
{
	return getY(p);
}

int CoreArray::getHowMuchOnRight(int* p)
{
	return width - getX(p) - 1;
}

int CoreArray::getHowMuchOnDown(int* p)
{
	return height - getY(p) - 1;
}

int CoreArray::getHowMuchOnLeft(int* p)
{
	return getX(p);
}

bool CoreArray::exchangeNum(int* p1, int* p2)
{
	if (!isInRange(p1) || !isInRange(p2))return false;
	int tmp = *p1;
	*p1 = *p2;
	*p2 = tmp;
	return true;
}

bool CoreArray::carryMove(int* p, int* pTarget) {
	Direction d = isMoveTo(p, pTarget);
	Direction rD = reverse(d);
	int carryNum = getCarryNumByDirection(p, rD);
	int deviant = p - pTarget;
	int* pCurrent = p;
	if (pCurrent == nullptr)return false;
	cout << " carry move - direction: " << ((int)d) << endl;
	cout << " carry move - caryyNum: " << carryNum << endl;
	cout << " carry move - deviant: " << deviant << endl;
	for (int i = 0; i < carryNum + 1; i++) {
		int* pTargetTmp = pCurrent - deviant;
		cout << " carry move - exchange - pCurrent : " << pCurrent << " : " << *pCurrent << endl;
		cout << " carry move - exchange - pTargetTmp : " << pTargetTmp << " : " << *pTargetTmp << endl;
		exchangeNum(pCurrent, pTargetTmp);
		pCurrent = getPByDirection(pCurrent, rD);
	}
	return true;
}
